﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace BobcatUtility
{
    /// <summary>
    /// This control hosts a vertical strip of controls and allows you to select 
    /// one of them.
    /// </summary>
    public partial class ControlStripControl : UserControl
    {
        #region Events

        /// <summary>
        /// Data passed with events raise by this control.
        /// </summary>
        public class Args : EventArgs
        {
            public UserControl Control { get; set; }
        }

        /// <summary>
        /// Event raised when the selection changes.
        /// </summary>
        public event EventHandler<Args> SelectionChangedEvent;

        #endregion

        #region Public properties

        /// <summary>
        /// Gets or sets the currently selected control.
        /// </summary>
        public UserControl SelectedControl
        {
            get
            {
                return m_SelectedControl;
            }
            set
            {
                // We don't set the new selection if this control is
                // already selected...
                if (m_SelectedControl == value)
                {
                    return;
                }
                // We simulate a click on the control, to select it...
                onClick(value, new EventArgs());
            }
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Constructor.
        /// </summary>
        public ControlStripControl()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Removes all controls from this control-strip.
        /// </summary>
        public void Clear()
        {
            unregisterClickEvents();
            Controls.Clear();
            m_iNextYPosition = SPACING;
            m_SelectedControl = null;
        }

        /// <summary>
        /// Adds a control to the control strip. 
        /// </summary>
        public void addControl(UserControl control)
        {
            // We set the position of the control, and add it to
            // the strip...
            control.Location = new Point(SPACING, m_iNextYPosition);
            Controls.Add(control);

            // We register for click events, so we can raise a 
            // selection-changed event when they occur...
            registerForClickEvent(control, control);

            // We work out the position of the next icon...
            m_iNextYPosition += control.Height;
            m_iNextYPosition += SPACING;
        }

        /// <summary>
        /// We set the selected icon to be the one at the index passed in.
        /// </summary>
        public void setSelectedIndex(int iIndex)
        {
            // Is the index valid?
            if (iIndex < 0 || iIndex >= Controls.Count)
            {
                return;
            }

            // We simulate a click on the icon...
            Control control = Controls[iIndex];
            onClick(control, new EventArgs());
        }

        #endregion

        #region Private functions

        /// <summary>
        /// We register for click events from the control passed in and any child 
        /// controls. 
        /// </summary>
        private void registerForClickEvent(Control control, UserControl parentControl)
        {
            // We register for the click event from the control passed in...
            control.Click += onClick;

            // We keep track of which child-controls are children of
            // which parents, so that when a click occurs, we can find which
            // top-level control it was associated with...
            m_mapChildControlToParentControl.Add(control, parentControl);

            // And from any child controls it has...
            foreach (Control childControl in control.Controls)
            {
                registerForClickEvent(childControl, parentControl);
            }
        }

        /// <summary>
        /// Unregisters us from the click events from all controls in the 
        /// control-strip.
        /// </summary>
        private void unregisterClickEvents()
        {
            foreach (Control control in Controls)
            {
                unregisterForClickEvent(control);
            }
        }

        /// <summary>
        /// Unregisters for click events from one control and all its children.
        /// </summary>
        private void unregisterForClickEvent(Control control)
        {
            // We unregister for the click event from the control passed in...
            control.Click -= onClick;

            // We remove the control from out map...
            m_mapChildControlToParentControl.Remove(control);

            // And from any child controls it has...
            foreach (Control childControl in control.Controls)
            {
                unregisterForClickEvent(childControl);
            }
        }

        /// <summary>
        /// Called when a control is clicked.
        /// </summary>
        void onClick(object sender, EventArgs e)
        {
            // We find the top-level control associated with the control that was clicked
            // (which could be a child control)...
            Control senderControl = (Control)sender;
            bool bHasControl = m_mapChildControlToParentControl.ContainsKey(senderControl);
            if (bHasControl == false)
            {
                throw new Exception("ControlStrip does not contain the control passed to onClick.");
            }
            m_SelectedControl = m_mapChildControlToParentControl[senderControl];

            // We show a border around the selected one (and clear borders on the others)
            foreach (UserControl c in Controls)
            {
                if (c == m_SelectedControl)
                {
                    c.BorderStyle = BorderStyle.FixedSingle;
                }
                else
                {
                    c.BorderStyle = BorderStyle.None;
                }
            }

            // We raise an event...
            Utility.CrossThreadInvoke(SelectionChangedEvent, this, new Args { Control = m_SelectedControl });
        }

        #endregion

        #region Private data

        private const int SPACING = 5;
        private int m_iNextYPosition = SPACING;
        private IDictionary<Control, UserControl> m_mapChildControlToParentControl = new Dictionary<Control, UserControl>();
        private UserControl m_SelectedControl = null;

        #endregion
    }
}
